/* Picaroon Map functions */

type TileType uint8
//define the types of map cell tiling - note: there are multiple ways to produce a Curly, Triangle, Square and Hex tiling
//(consider the rotational symmetry). We might want to distingush them.
const (
	TT_Line TileType = iota // a 1d map!
	TT_Curly //weird 2d tiling with unidirectional boundaries
	TT_Triangle //triangles (3 tiles meet)
	TT_Square //4 way movement
	TT_Hexagon //6 way movement
	TT_Octagon //8 way movement
)

type Cell struct {
	terrain TerrainType
	creature Creature
	item Item
}

type Grcolour struct { //move me to a better source file
	R,G,B,A uint8 //only need 32bit colour
}

type Sort struct {
	Sort rune
	Fg,Bg Grcolour //could make these slices of Grcolour (and same for Sort) to allow alternation
	//for example, to do fancy "animated water" etc (obv non-animated stuff can be done with single Fg,Bg values per Sort (just pick from
	// a range of values per terrain type on assignment)
}

type Map struct {
	rawmap [][]Cell
	tiling TileType
	m Metric
}

type MapView interface {
	Cell(c Coord) Cell, bool
	Dimensions() Coord
	Metric() Metric
}

type MapCtrl interface {
	MapView
	SetCell(c Coord, cell Cell) bool
	SetMetric(m Metric) bool
}


func (m Map) Cell(c Coord) Cell, bool {
	if (c.X < len(m.rawmap) ) and (c.Y < len(m.rawmap[0])) {
		return m.rawmap[c.X][c.Y], true
		}
	return Cell{}, false
	}

func (m Map) Dimensions() Coord {
	return Coord{len(m.rawmap), len(m.rawmap[0])}

func (m Map) Metric() Metric {
	return m.m
	}

func (m * Map) SetCell(c Coord, cell Cell) bool {
	if (c.X < len(m.rawmap) ) and (c.Y < len(m.rawmap[0])) {
		m.rawmap[c.X][c.Y] = cell
		return true
		}
	return false
	}

func (m * Map) SetMetric(m Metric) bool {
	m.m = m // need to set tiling - but tiling is basically redundant with metric - decide how this works
	return true
	}

	
//metrics are almost certainly an interface type


//metric needs to provide a distance, an adjacency and a cell set for lighting/other propagating beams
type Arc struct { //really not sure about this - perhaps Annulus needs to return a map[angle] coord where angle is the start angle
	coord Coord
	start_angle uint16
	end_angle uint16
}

type Metric interface {
	Distance(a, b Coord) uint8
	Adjacency(a Coord) []Coord
	Annulus(a Coord, radius uint8) []Arc
}


const Orthogonal struct {
	annulus_data [][]Arc = //cache of annulusen out to max_width of maps - 64 or 128 or so 
	}

func (o Orthogonal) Distance (a,b Coord) uint8 {
	return (math.Abs(a.X - b.X) + math.Abs(a.Y-b.Y) )
}

func (o Orthogonal) Adjacency(a Coord) []Coord //adjacent cells are those in (+/- 1, 0), (0, +/-1), as long as they're in the map range
	l := make([]Coord, 0,4)
	tmp := a
	if tmp.Sub(Coord{1,0}) == true {
		append(l,tmp)
		}
	tmp = a
	if a.Add(Coord{0,1}) == true {
                append(l,tmp)
        	}
	tmp = a
	if a.Add(Coord{1,0}) == true {
                append(l,tmp)
        	}
	tmp = a
	if a.Sub(Coord{0,1}) == true {
                append(l,tmp)
        	}
	return l
	} 

func (o Orthogonal) Annulus(a Coord, radius uint8) []Arc {
	template := o.annulus_data[radius]
	trans := Coord{radius,radius} //coords can't be negative, so we do "negative Coords" by having our annulusen all translated back by (-radius,-radius) after
				// so {-radius,-radius} is stored as {0,0} in the slice
	for i := range(template) {
		template[i].Coord.Add(a)
		if template[i].Coord.Sub(trans) == false { //the coord isn't in the map range anymore, so not a valid coord
		 	template = append(template[0:i],template[i+1:]...) //so cut it out of our annulus return
			}
		}
	//now our template contains the valid cells coords with respect to a
	return template
}

type DisplayBufferView interface {
	Dimensions() Coord //X,Y dimensions
	Display() [][]Sort //"Sort" is a technical word for "character". In this case, we're using it for the (rune,fg,bg) triplet type for displays
	CellAt(c Coord) Cell //probably actually want to just return info about the Cell, not the Cell (this is a read-only interface)
	SortAt(c Coord) Sort, bool
}


type DisplayBufferCtrl interface {
	DisplayBufferView
	SetSort(c Coord, s Sort) bool
}

type DisplayBuffer struct {
	display [][]Sort
	map	* MapView //a MapView interface for the map the DisplayBuffer is based on
}


func (db DisplayBuffer) Dimensions() Coord {
	return Coord{len(db.display), len(db.display[0])}
	}

func (db DisplayBuffer) Display() [][]Sort {
	s = make([][]Sort,len(db.display))
	for i := range s {
		s[i] = make([]Sort,len(db.display[0]))
		for j in range s[i] {
			s[i][j] = db.display[i][j]
		}
	}
	return s // this whole nonsense is needed to ensure that the returned [][]Sort cannot modify the copy in the DisplayBuffer, as Go has no const specifier
}

func (db DisplayBuffer) CellAt(c Coord) Cell, bool {
	if (c.X < len(db.display) and (c.Y < len(db.display[0]) {
		db.map(lookup cell)
		return c, true
		}
	return Cell{}, false
	}

func (db DisplayBuffer) SortAt(c Coord) Sort, bool {
	if (c.X < len(db.display) and (c.Y < len(db.display[0]) {
		s = db.display[c.X][c.y]
		return s, true
		} 
	return Sort{}, false
	}

func (db * DisplayBuffer) SetSort(c Coord, s Sort) bool
	if (c.X < len(db.display) and (c.Y < len(db.display[0]) {
		db.display[c.X][c.Y] = s
		return true
		}
	return false
	}
